<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>

    <body>
        <div>https://segmentfault.com/a/1190000024498621</div>
        <div>https://www.cnblogs.com/dobeco/p/11295316.html</div>
        <div>作者：邹润阳 链接：https://www.zhihu.com/question/23031215/answer/46220227 来源：知乎</div>
        <div></div>
        <script>
            //   浅拷贝
            let a = { xx: 1, yy: 2, zz: { nn: 3, mm: 4 } };
            console.log(a);
            let b = a;
            let c = Object.assign({}, a);
            console.log(a);
            console.log(b);
            console.log(c);
            b.xx = 88;
            c.yy = 99;
            b.zz.nn = 666;
            c.zz.mm = 777;
            console.log('a    : ', a);
            console.log('a.zz : ', a.zz);
            console.log(b);
            console.log(c);
            console.log(c.zz);

            var obj = { a: 1, arr: [2, 3], x: { y: { z: 2 } } };
            var shallowObj = shallowCopy(obj);
            console.log(obj);
            shallowObj.arr[1] = 555;
            obj.arr[1]; // = 5
            console.log(obj.arr[1]);

            function shallowCopy(src) {
                var dst = {};
                for (var prop in src) {
                    if (src.hasOwnProperty(prop)) {
                        dst[prop] = src[prop];
                    }
                }
                return dst;
            }

            // 缺点：
            // （1）无法保持引用
            // （2）当数据的层次很深，会栈溢出
            function isObject(obj) {
                return typeof obj === 'object' && obj !== null;
            }
            function deepCopy1(source) {
                if (!isObject(source)) return source; //如果不是对象的话直接返回
                let target = Array.isArray(source) ? [] : {}; //数组兼容
                for (var k in source) {
                    if (source.hasOwnProperty(k)) {
                        if (typeof source[k] === 'object') {
                            target[k] = deepCopy1(source[k]);
                        } else {
                            target[k] = source[k];
                        }
                    }
                }
                return target;
            }

            function deepClone2(source) {
                if (typeof source !== 'object') {
                    // 非对象类型(undefined、boolean、number、string、symbol)，直接返回原值即可
                    return source;
                }
                if (source === null) {
                    // 为null类型的时候
                    return source;
                }
                if (source instanceof Date) {
                    // Date类型
                    return new Date(source);
                }
                if (source instanceof RegExp) {
                    // RegExp正则类型
                    return new RegExp(source);
                }
                let result;
                if (Array.isArray(source)) {
                    // 数组
                    result = [];
                    source.forEach((item) => {
                        result.push(deepClone2(item));
                    });
                    return result;
                } else {
                    // 为对象的时候
                    result = {};
                    // 取出对象的key以及symbol类型的key
                    const keys = [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)];
                    keys.forEach((key) => {
                        let item = source[key];
                        result[key] = deepClone2(item);
                    });
                    return result;
                }
            }
            // let abc = { name: 'a', msg: { name: 'lihb' }, date: new Date('2020-09-17'), reg: new RegExp(/123/) };
            // let bbb = deepClone(abc);
            // abc.msg.name = 'lily';
            // abc.date = new Date('2020-08-08');
            // abc.reg = new RegExp(/456/);
            // console.log(bbb);
            // { name: 'a', msg: { name: 'lihb' }, date: 2020-09-17T00:00:00.000Z, reg: /123/ }

            function checktype(obj) {
                //检查对象类型
                return Object.prototype.toString.call(obj).slice(8, -1);
            }
            function depCopy3(target, hash = new WeakMap()) {
                //hash 作为一个检查器，避免对象深拷贝中出现环引用，导致爆栈。
                let type = checktype(target);
                let result = null;
                if (type == 'Object') {
                    result = {};
                } else if (type == 'Array') {
                    result = [];
                } else {
                    return target;
                }
                if (hash.has(target)) {
                    //检查是有存在相同的对象在之前拷贝过，有则返回之前拷贝后存于hash中的对象
                    return hash.get(target);
                }
                hash.set(target, result); //备份存在hash中，result目前是空对象、数组。后面会对属性进行追加，这里存的值是对象的栈
                for (let i in target) {
                    if (checktype(target[i]) == 'Object' || checktype(target[i]) == 'Array') {
                        result[i] = depCopy3(target[i], hash); //属性值是对象，进行递归深拷贝
                    } else {
                        result[i] = target[i]; //其他类型直接拷贝
                    }
                }
                return result;
            }

            var ccb = { gg: 123, ff: a };
            var cca = {
                c: {
                    k: 1,
                    k2: 2,
                },
                d: {
                    d1: [1, 2],
                    d2: ccb,
                    d3: {
                        f1: 'kkk',
                    },
                },
            };

            let cop = depCopy3(cca);

            function deepClone(source, map = new WeakMap()) {
                // 传入一个WeakMap对象用于记录拷贝前和拷贝后的映射关系
                if (typeof source !== 'object') {
                    // 非对象类型(undefined、boolean、number、string、symbol)，直接返回原值即可
                    return source;
                }
                if (source === null) {
                    // 为null类型的时候
                    return source;
                }
                if (source instanceof Date) {
                    // Date类型
                    return new Date(source);
                }
                if (source instanceof RegExp) {
                    // RegExp正则类型
                    return new RegExp(source);
                }
                if (map.get(source)) {
                    // 如果存在相互引用，则从map中取出之前拷贝的结果对象并返回以便形成相互引用关系
                    return map.get(source);
                }
                let result;
                if (Array.isArray(source)) {
                    // 数组
                    result = [];
                    map.set(source, result); // 数组也会存在相互引用
                    source.forEach((item) => {
                        result.push(deepClone(item, map)); // 必须传入第一次调用deepClone时候创建的map对象
                    });
                    return result;
                } else {
                    // 为对象的时候
                    result = {};
                    map.set(source, result); // 保存已拷贝的对象
                    const keys = [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)]; // 取出对象的key以及symbol类型的key
                    keys.forEach((key) => {
                        let item = source[key];
                        result[key] = deepClone(item, map); // 必须传入第一次调用deepClone时候创建的map对象
                    });
                    return result;
                }
            }

            let aob = { aa: 1, bb: 3 };
            let aoc = [11, 22, 33];
            console.log(aob);
            console.log(aoc);
            for (const key in aob) {
                console.log(key, aob[key]);
            }
            for (const key in aoc) {
                console.log(key, aoc[key]);
            }
        </script>
    </body>
</html>
